// -*- mode: C++ -*-
//
// Copyright (c) 2008, 2010, 2011 The University of Utah
// All rights reserved.
//
// This file is part of `csmith', a random generator of C programs.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//
//   * Redistributions in binary form must reproduce the above copyright
//     notice, this list of conditions and the following disclaimer in the
//     documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

///////////////////////////////////////////////////////////////////////////////

#include "CGOptions.h"
#include <iostream>
#include <assert.h>
#include "Fact.h"
#include "DefaultOutputMgr.h"
#include "CompatibleChecker.h"
#include "PartialExpander.h"
#include "DeltaMonitor.h"
#include "Probabilities.h"
#include "OutputMgr.h"
#include "StringUtils.h"

using namespace std;
Reducer* CGOptions::reducer_ = NULL;
vector<int> CGOptions::safe_math_wrapper_ids_;

/*
 *
 */
#define DEFINE_GETTER_SETTER(type, init, f)	\
	type CGOptions::f##_ = (init); \
	type \
	CGOptions::f(void) { return f##_; }	\
	type \
	CGOptions::f(type p) { f##_ = p; return p; }

/*
 *
 */
#define DEFINE_GETTER_SETTER_BOOL(f) \
	DEFINE_GETTER_SETTER(bool, false, f)

#define DEFINE_GETTER_SETTER_INT(f) \
	DEFINE_GETTER_SETTER(int, 0, f)

#define DEFINE_GETTER_SETTER_STRING_REF(f) \
	DEFINE_GETTER_SETTER(std::string, "", f)

///////////////////////////////////////////////////////////////////////////////

DEFINE_GETTER_SETTER_BOOL(compute_hash)
DEFINE_GETTER_SETTER_BOOL(depth_protect)
DEFINE_GETTER_SETTER_INT (max_split_files)
DEFINE_GETTER_SETTER_STRING_REF(split_files_dir)
DEFINE_GETTER_SETTER_STRING_REF(output_file)
DEFINE_GETTER_SETTER_INT (max_funcs)
DEFINE_GETTER_SETTER_INT (max_params)
DEFINE_GETTER_SETTER_INT (max_block_size)
DEFINE_GETTER_SETTER_INT (max_stmt_depth)
DEFINE_GETTER_SETTER_INT (max_expr_depth)
DEFINE_GETTER_SETTER_BOOL(wrap_volatiles)
DEFINE_GETTER_SETTER_BOOL(allow_const_volatile)
DEFINE_GETTER_SETTER_BOOL(avoid_signed_overflow)
DEFINE_GETTER_SETTER_INT (max_struct_fields)
DEFINE_GETTER_SETTER_INT (max_nested_struct_level)
DEFINE_GETTER_SETTER_STRING_REF(struct_output)
DEFINE_GETTER_SETTER_BOOL (fixed_struct_fields)
DEFINE_GETTER_SETTER_BOOL (expand_struct)
DEFINE_GETTER_SETTER_BOOL (use_struct)
DEFINE_GETTER_SETTER_INT (max_indirect_level)
DEFINE_GETTER_SETTER_INT (max_array_dimensions)
DEFINE_GETTER_SETTER_INT (max_array_length_per_dimension)
DEFINE_GETTER_SETTER_INT (max_array_length)
DEFINE_GETTER_SETTER_INT (interested_facts)
DEFINE_GETTER_SETTER_BOOL(paranoid)
DEFINE_GETTER_SETTER_BOOL(quiet)
DEFINE_GETTER_SETTER_BOOL(concise)
DEFINE_GETTER_SETTER_BOOL(nomain)
DEFINE_GETTER_SETTER_BOOL(random_based)
DEFINE_GETTER_SETTER_BOOL(dfs_exhaustive)
DEFINE_GETTER_SETTER_STRING_REF(dfs_debug_sequence)
DEFINE_GETTER_SETTER_INT (max_exhaustive_depth)
DEFINE_GETTER_SETTER_BOOL(compact_output)
DEFINE_GETTER_SETTER_BOOL(msp)
DEFINE_GETTER_SETTER_INT(func1_max_params)
DEFINE_GETTER_SETTER_BOOL(splat)
DEFINE_GETTER_SETTER_BOOL(klee)
DEFINE_GETTER_SETTER_BOOL(crest)
DEFINE_GETTER_SETTER_BOOL(ccomp)
DEFINE_GETTER_SETTER_BOOL(coverage_test)
DEFINE_GETTER_SETTER_INT(coverage_test_size)
DEFINE_GETTER_SETTER_BOOL(packed_struct)
DEFINE_GETTER_SETTER_BOOL(bitfields)
DEFINE_GETTER_SETTER_INT(bitfields_length)
DEFINE_GETTER_SETTER_BOOL(prefix_name)
DEFINE_GETTER_SETTER_BOOL(sequence_name_prefix)
DEFINE_GETTER_SETTER_BOOL(compatible_check)
DEFINE_GETTER_SETTER_STRING_REF(partial_expand)
DEFINE_GETTER_SETTER_STRING_REF(delta_monitor)
DEFINE_GETTER_SETTER_STRING_REF(delta_output)
DEFINE_GETTER_SETTER_STRING_REF(go_delta)
DEFINE_GETTER_SETTER_STRING_REF(delta_input)
DEFINE_GETTER_SETTER_BOOL(no_delta_reduction)
DEFINE_GETTER_SETTER_BOOL(math64)
DEFINE_GETTER_SETTER_BOOL(math_notmp)
DEFINE_GETTER_SETTER_BOOL(longlong)
DEFINE_GETTER_SETTER_BOOL(pointers)
DEFINE_GETTER_SETTER_BOOL(arrays)
DEFINE_GETTER_SETTER_BOOL(strict_const_arrays)
DEFINE_GETTER_SETTER_BOOL(jumps)
DEFINE_GETTER_SETTER_BOOL(return_structs)
DEFINE_GETTER_SETTER_BOOL(arg_structs)
DEFINE_GETTER_SETTER_BOOL(volatiles)
DEFINE_GETTER_SETTER_BOOL(volatile_pointers)
DEFINE_GETTER_SETTER_BOOL(enable_vol_tests)
DEFINE_GETTER_SETTER_STRING_REF(vol_addr_file)
DEFINE_GETTER_SETTER_BOOL(consts)
DEFINE_GETTER_SETTER_BOOL(dangling_global_ptrs)
DEFINE_GETTER_SETTER_BOOL(divs)
DEFINE_GETTER_SETTER_BOOL(muls)
DEFINE_GETTER_SETTER_BOOL(accept_argc)
DEFINE_GETTER_SETTER_BOOL(random_random)
DEFINE_GETTER_SETTER_INT(stop_by_stmt) 
DEFINE_GETTER_SETTER_BOOL(deputy) 
DEFINE_GETTER_SETTER_BOOL(step_hash_by_stmt)
DEFINE_GETTER_SETTER_BOOL(compound_assignment)
DEFINE_GETTER_SETTER_STRING_REF(dump_default_probabilities)
DEFINE_GETTER_SETTER_STRING_REF(dump_random_probabilities)
DEFINE_GETTER_SETTER_STRING_REF(probability_configuration)
DEFINE_GETTER_SETTER_BOOL(const_as_condition)
DEFINE_GETTER_SETTER_BOOL(match_exact_qualifiers)
DEFINE_GETTER_SETTER_BOOL(blind_check_global)
DEFINE_GETTER_SETTER_BOOL(no_return_dead_ptr)
DEFINE_GETTER_SETTER_BOOL(identify_wrappers)
DEFINE_GETTER_SETTER_BOOL(mark_mutable_const)

void
CGOptions::set_default_settings(void)
{
	compute_hash(true);
	max_funcs(CGOPTIONS_DEFAULT_MAX_SPLIT_FILES);
	max_funcs(CGOPTIONS_DEFAULT_MAX_FUNCS);
	max_params(CGOPTIONS_DEFAULT_MAX_PARAMS);
	max_block_size(CGOPTIONS_DEFAULT_MAX_BLOCK_SIZE);
	max_stmt_depth(CGOPTIONS_DEFAULT_MAX_STMT_DEPTH);
	max_expr_depth(CGOPTIONS_DEFAULT_MAX_EXPR_DEPTH);
	max_struct_fields(CGOPTIONS_DEFAULT_MAX_STRUCT_FIELDS);
	max_nested_struct_level(CGOPTIONS_DEFAULT_MAX_NESTED_STRUCT_LEVEL);
	fixed_struct_fields(false);
	expand_struct(false);
	max_array_dimensions(CGOPTIONS_DEFAULT_MAX_ARRAY_DIMENSIONS);
	max_array_length_per_dimension(CGOPTIONS_DEFAULT_MAX_ARRAY_LENGTH_PER_DIMENSION);
	max_array_length(CGOPTIONS_DEFAULT_MAX_ARRAY_LENGTH);
	max_exhaustive_depth(CGOPTIONS_DEFAULT_MAX_EXHAUSTIVE_DEPTH);
	max_indirect_level(CGOPTIONS_DEFAULT_MAX_INDIRECT_LEVEL);
	output_file(CGOPTIONS_DEFAULT_OUTPUT_FILE);
	interested_facts(ePointTo);
	allow_const_volatile(true);
	avoid_signed_overflow(true);
	CGOptions::paranoid(false);
	CGOptions::quiet(false);
	CGOptions::concise(false);
	CGOptions::nomain(false);
	random_based(true);
	use_struct(true);
	compact_output(false);
	msp(false);
	func1_max_params(CGOPTIONS_DEFAULT_FUNC1_MAX_PARAMS);
	splat(false);
	klee(false);
	crest(false);
	ccomp(false);
	coverage_test(false);
	coverage_test_size(CGOPTIONS_DEFAULT_COVERAGE_TEST_SIZE);
	packed_struct(false);
	bitfields(false);
	prefix_name(false);
	sequence_name_prefix(false);
	compatible_check(false);
	compound_assignment(true);
	math64(true);
	math_notmp(false);
	longlong(true);
	pointers(true);
	arrays(true);
	strict_const_arrays(false);
	jumps(true);
	return_structs(true);
	arg_structs(true);
	volatiles(true);
	volatile_pointers(true);
	vol_addr_file(CGOPTIONS_DEFAULT_VOL_ADDR_FILE);
	consts(true);
	dangling_global_ptrs(true);
	divs(true);
	muls(true);
	accept_argc(true);
	stop_by_stmt(-1);
	deputy(false);
	step_hash_by_stmt(false);
	const_as_condition(false);
	match_exact_qualifiers(false);
	blind_check_global(false);
	no_return_dead_ptr(true);
	identify_wrappers(false);
	mark_mutable_const(false);
}

#define MAX_INTEGER_LENGTH 64

bool
CGOptions::resolve_bitfields_length(void)
{
	CGOptions::bitfields_length(MAX_INTEGER_LENGTH - 1);
	return true;
}

bool
CGOptions::is_random(void)
{
	return random_based_;
}

std::string CGOptions::conflict_msg_ = "";

const std::string&
CGOptions::conflict_msg(void)
{
	return conflict_msg_;
}

bool CGOptions::has_random_based_conflict(void)
{
	bool rv = false;
	if (!CGOptions::random_based()) {
		if (!CGOptions::split_files_dir().empty()) {
			conflict_msg_ = "split_files_dir ";
			rv = true;
		}
		else if (CGOptions::max_split_files() > 0) {
			conflict_msg_ = "max_split_files ";
			rv = true;
		}
		if (rv) {
			conflict_msg_ += "can only be applied to random mode";
			return rv;
		}
	}
	else {
		if (CGOptions::sequence_name_prefix()) {
			conflict_msg_ = "--sequence-name-prefix option can only be \
					used with --dfs-exhaustive";
			rv = true;
		}
	}
	return rv;
}

bool CGOptions::resolve_exhaustive_options()
{
	if (!CGOptions::dfs_exhaustive())
		return false;

	bool rv = false;

	if (CGOptions::compatible_check())
		CompatibleChecker::enable_compatible_check();
	if (!CGOptions::partial_expand().empty()) {
		if (!PartialExpander::init_partial_expander(CGOptions::partial_expand())) {
			conflict_msg_ = "wrong partial-expand options!";
			return true;
		}
	}
	if (CGOptions::max_exhaustive_depth() <= 0) {
		conflict_msg_ = "max-exhaustive-depth must be at least 0";
		return true;
	}

	if (!CGOptions::use_struct() && CGOptions::expand_struct()) {
		conflict_msg_ = "expand-struct cannot be used with --no-struct";
		return true;
	}
	
	if (CGOptions::has_extension_support()) {
		conflict_msg_ = "exhaustive mode doesn't support splat|klee|crest|coverage-test extension";
		return true;
	}
	// For effeciency reason, we fix the size of struct fields
	CGOptions::fixed_struct_fields(true);
	return rv;
}

bool
CGOptions::has_delta_conflict()
{
	if (!CGOptions::delta_monitor().empty() &&
		!CGOptions::go_delta().empty()) {
		conflict_msg_ = "you cannot specify --delta-monitor and \
			--go-delta monitor at the same time. --go-delta \
			will invoke the corresponding delta monitor implicitly.";
		return true;
	}
	return false;
}

bool
CGOptions::has_extension_conflict()
{
	int count = 0;

	if (CGOptions::splat())
		count++;
	if (CGOptions::klee())
		count++;
	if (CGOptions::crest())
		count++;
	if (CGOptions::coverage_test())
		count++;

	if (count > 1) {
		conflict_msg_ = "You could only specify --splat or --klee or --crest or --coverage-test";
		return true;
	}
	return false;
}

bool
CGOptions::has_extension_support()
{
	return (CGOptions::splat() || CGOptions::klee() 
		|| CGOptions::crest() || CGOptions::coverage_test());
}

bool
CGOptions::allow_int64()
{
	if (CGOptions::ccomp()) 
		return false; 
	return ((!CGOptions::has_extension_support()) &&
			CGOptions::math64() && 
			CGOptions::longlong());
}

bool
CGOptions::has_conflict(void)
{
	if (CGOptions::has_random_based_conflict())
		return true;

	if (resolve_exhaustive_options())
		return true;

	if (CGOptions::has_extension_conflict())
		return true;

	if (CGOptions::has_delta_conflict())
		return true;

	if (CGOptions::max_funcs() < 1) {
		conflict_msg_ = "max-funcs must be at least 1";
		return true;
	}
	if (CGOptions::max_stmt_depth() < 1) {
		conflict_msg_ = "max-stmt-depth must be at least 1";
		return true;
	}

	if (CGOptions::func1_max_params() > CGOptions::max_params()) {
		conflict_msg_ = "func1_max_params() cannot be larger than max_params()";
		return true;
	}
	if (CGOptions::split_files_dir().empty()) {
		CGOptions::split_files_dir(CGOPTIONS_DEFAULT_SPLIT_FILES_DIR);
	}
	if (CGOptions::max_split_files() > 0) {
		if (!DefaultOutputMgr::create_output_dir(CGOptions::split_files_dir())) {
			conflict_msg_ = "cannot create dir for split files!";
			return true;
		}
	}
	
	if (!CGOptions::delta_monitor().empty()) {
		string msg;
		if (!DeltaMonitor::init(msg, CGOptions::delta_monitor(), CGOptions::delta_output())) {
			conflict_msg_ = msg;
			return true;
		}
	}

	if (!CGOptions::go_delta().empty()) {
		string msg;
		if (!DeltaMonitor::init_for_running(msg, CGOptions::go_delta(), CGOptions::delta_output(), 
				CGOptions::delta_input(), CGOptions::no_delta_reduction())) {
			conflict_msg_ = msg;
			return true;
		}
	}

#if 0
	Probabilities *prob = Probabilities::GetInstance();
	if (!(CGOptions::dump_default_probabilities().empty()))
		prob->dump_default_probabilities(CGOptions::dump_default_probabilities());

	string msg;
	if (!(CGOptions::probability_configuration().empty())) {
		if (!prob->parse_configuration(msg, CGOptions::probability_configuration())) {
			conflict_msg_ = msg;
			return true;
		}
	}
#endif

	return false;
}

void
CGOptions::monitored_funcs(std::string fnames)
{
	parse_string_options(fnames, OutputMgr::monitored_funcs_);
}

void 
CGOptions::parse_string_options(string vname, vector<std::string> &v)
{
	if (vname.empty()) return;
	// parse the string if there are multiple variable names in format "v1,v2,..." 
	size_t pos1 = 0;
	size_t pos2 = 0;
	do {
		pos2 = vname.find_first_of(',', pos1); 
		string var_name = (pos2 != string::npos) ? vname.substr(pos1, pos2 - pos1) : vname.substr(pos1);
		v.push_back(var_name);
		pos1 = pos2 + 1;
	} while (pos2 != string::npos);
}

bool
CGOptions::x86_64()
{
#if defined __x86_64__
	return true;
#else
	return false;
#endif
}

std::string CGOptions::vol_tests_mach_ = "";

std::string 
CGOptions::vol_tests_mach(void)
{
	return CGOptions::vol_tests_mach_;
}

bool
CGOptions::set_vol_tests(const string &s)
{
	if (!s.compare("x86") || !s.compare("x86_64")) {
		CGOptions::enable_vol_tests(true);
		CGOptions::vol_tests_mach_ = s;
		return true;
	}
	else {
		return false;
	}
}

void 
CGOptions::safe_math_wrapper(string ids)
{
	StringUtils::split_int_string(ids, safe_math_wrapper_ids_, ",");
}

bool 
CGOptions::safe_math_wrapper(int id)
{
	// if no safe math wrapper ids specified, assume all needs wrapper
	if (safe_math_wrapper_ids_.empty()) return true;
	return std::find(safe_math_wrapper_ids_.begin(), safe_math_wrapper_ids_.end(), id) != safe_math_wrapper_ids_.end();
}

///////////////////////////////////////////////////////////////////////////////

// Local Variables:
// c-basic-offset: 4
// tab-width: 4
// End:

// End of file.
